package edu.fub.pub2search.app;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.OverlayItem;

import edu.fub.pub2search.app.util.AndroidIOUtil;
import edu.fub.pub2search.app.util.JSONUtil;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.location.Address;
import android.location.Geocoder;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.widget.EditText;

/**
 * Main activity to manage all functionalities of Pub2Search. 
 * 
 * @author Andrej
 */
public class DisplayPub extends MapActivity {
	
	/** Constants for menu */
	private static final	int MENU_SEARCH = 0; 
	private static final	int MENU_RADIUS = 1;
		private static final	int SUBMENU_RADIUS_0 = 10;
		private static final	int SUBMENU_RADIUS_1 = 11;
		private static final	int SUBMENU_RADIUS_2 = 12;
		private static final	int SUBMENU_RADIUS_3 = 13;
	private static final	int MENU_FILTER = 2;
	
	/** Constants for radius in meters */
	private static final int RADIUS_0 = 100;
	private static final int RADIUS_1 = 200;
	private static final int RADIUS_2 = 300;
	private static final int RADIUS_3 = 500;
	
	/** Zoom value for appropriate radius, <radius, zoomLevel> */
	private HashMap<Integer, Integer> radiusZoomValues; 
	
	/** Bolzano Geo-Position, default location */
	//private static final GeoPoint BOLZANO_GEOPOINT = new GeoPoint(46495166, 11354103);
	static final int PREFERENCE_ACTIVITY = 12;
		
	/** Constants for dialog */
	private static final int DIALOG_ADDRESS_ENTRY = 0;
	private static final int DIALOG_ALLERT = 1;
	
	/** Variables */
	private List<Map<String, String>> pubList;
	private int radius;		// in meters
	private GeoPoint userLocation = null;
	private MapViewer mapViewer;
	
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    	/*
    	 * Called when the activity is first created.
    	 */
        
        // Using maps setting by XML file    
        setContentView(R.layout.main);
        
        // Assign LinearLayout to mapViewer
        mapViewer = (MapViewer) findViewById(R.id.map_viewer);
        
        radiusZoomValues = new HashMap<Integer, Integer>();
        radiusZoomValues.put(RADIUS_0, 21);
        radiusZoomValues.put(RADIUS_1, 17);
        radiusZoomValues.put(RADIUS_2, 17);
        radiusZoomValues.put(RADIUS_3, 16);
        
    }
    
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu){
    	super.onCreateOptionsMenu(menu);
    	
    	menu.removeGroup(Menu.CATEGORY_SYSTEM);
    	
    	
    	// Main menu: Search, Radius, Preference
        menu.add(0, MENU_SEARCH, 0, "Search" )
        	.setIcon(R.drawable.ic_menu_search);
        
        SubMenu sub = menu.addSubMenu(0, MENU_RADIUS, 1, "Radius")
						  .setIcon(R.drawable.ic_menu_radius);
        sub.add(1, SUBMENU_RADIUS_0, 0, "100 metres");
        sub.add(1, SUBMENU_RADIUS_1, 1, "200 metres");
        sub.add(1, SUBMENU_RADIUS_2, 2, "300 metres");
        sub.add(1, SUBMENU_RADIUS_3, 3, "500 metres");
        
        menu.add(0, MENU_FILTER, 2, "Preference")
        	.setIcon(R.drawable.ic_menu_directions);
        
        // Set SUBMENU_RADIUS_1 as a default
        sub.setGroupCheckable(1, true, true);
        menu.findItem(SUBMENU_RADIUS_1).setChecked(true);
        radius = RADIUS_1; // default value
        
        return true;
    }
    
    

    @Override
    public boolean onOptionsItemSelected(MenuItem item){
    	super.onOptionsItemSelected(item); 
    	/*
    	 *	Called when item in menu was selected. 
    	 */
    	
    	   	switch (item.getItemId()) {
	        case MENU_SEARCH:
	        	// Open dialog: Search for address
	        	showDialog(DIALOG_ADDRESS_ENTRY); 	
	            return true;
	            
	        case SUBMENU_RADIUS_0:
	        	// Call pub2Search database
	        	if (userLocation != null) {
	        		radius = RADIUS_0;
		        	item.setChecked(true);
	        		callService();	        		
	        		return true;
	        	}
	        	break;
	        	
	        case SUBMENU_RADIUS_1:	        	
	        	if (userLocation != null) {
	        		radius = RADIUS_1;
		        	item.setChecked(true);
	        		callService();	
	        		return true;
	        	}
	        	break;
	        	
	        case SUBMENU_RADIUS_2:	        	
	        	if (userLocation != null) {
	        		radius = RADIUS_2;
		        	item.setChecked(true);
	        		callService();
	        		return true;
	        	}
	        	break;
	      	
	        case SUBMENU_RADIUS_3:
	        	if (userLocation != null) {
	        		radius = RADIUS_3;
		        	item.setChecked(true);
		        	callService();	
	        		return true;
	        	}
	        	break;
	        	
	        case MENU_FILTER: 
	           	if (userLocation != null) {
	           		// Start new Preference Activity 
	           		Intent preference = new Intent(this, Preference.class);
	           		startActivityForResult (preference,PREFERENCE_ACTIVITY);
	           		return true;
	           	}
	           	break;
	           	
	        case MENU_RADIUS:
	        	return true;
	    	}   	
	    	
	    // Dialog alert when userLocation was not searched
	    if (userLocation == null)
	    	showDialog(DIALOG_ALLERT);
        return false;
    }
    
    

	@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        /*
         *	Method called when child activity send back resultCode  
         */
    	
        if (requestCode == PREFERENCE_ACTIVITY) {
            if (resultCode == RESULT_OK) {
            	// On finish Preference Activity update Map
            	updateScreenMap();
            }
        }
    }
    
    

    @Override
    protected Dialog onCreateDialog(int id) {
        /*
         *	Method for creating dialogs 
         */
    	
    	LayoutInflater factory;
    	switch (id) {
    	case DIALOG_ADDRESS_ENTRY:
    		// Build dialog: Search for address 
            factory = LayoutInflater.from(this);
            final View textEntryView = factory.inflate(R.layout.search, null);
            return new AlertDialog.Builder(DisplayPub.this)
                .setView(textEntryView)
                .setPositiveButton(R.string.dialog_search, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int whichButton) {
                    	// Extract string from inpt field and search
                    	EditText addressEdit = (EditText) textEntryView.findViewById(R.id.address_edit); 
                    	String personAddress = addressEdit.getText().toString() + ", " + getText(R.string.default_city);
                    	
                    	// Set new person location 
                    	findPersonLocation(personAddress);
                    	// Call service for fetch pubs from database
                    	callService();
                    }
                })
                .create();
         
	    case DIALOG_ALLERT:
	    	factory = LayoutInflater.from(this);
            final View textView = factory.inflate(R.layout.allert, null);
            return new AlertDialog.Builder(DisplayPub.this)
                .setView(textView)
                .setPositiveButton(R.string.dialog_ok, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int whichButton) {
                    	//close();
                    }
                })             
                .create();
		}
    	
		return null;
    }
    
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent msg) {
    	super.onKeyDown(keyCode, msg);
    	MapViewer mapViewer = (MapViewer) findViewById(R.id.map_viewer);
    	if(keyCode == KeyEvent.KEYCODE_DPAD_UP){
    		mapViewer.getMapView().getController().zoomIn();
    		return true;
    	}
    	if(keyCode == KeyEvent.KEYCODE_DPAD_DOWN){
    		mapViewer.getMapView().getController().zoomOut();    	
			return true;
    	}
    	return false;
    }
    
    @Override
    protected boolean isRouteDisplayed() {
        // TODO Auto-generated method stub
        return false;
    }

// ------------------- PRIVATE METHOD ------------------------
    
    /*
     * Method to set zoom level according the chosen radius
     */
    private void setZoomLevelOnRadius(int radius) {
		int zoomLevel = radiusZoomValues.get(radius);
    	mapViewer.getMapView().getController().setZoom(zoomLevel);
	}

    /*
     * Method to gain GeoPoint(latitude,longitude) based from the input address.
     */
    private void findPersonLocation(String address) {
    	
    	Geocoder gc = new Geocoder(this, Locale.getDefault());
    	
    	// Get the person position (lat, lng) based from the input address
	    try {
	    	List<Address> addressResult = gc.getFromLocationName(address, 1);
	    	if (!addressResult.isEmpty()) {
	            Address resultAddress = addressResult.get(0);
	            
	            int userLatitude = (int) (resultAddress.getLatitude() * 1E6);
	            int userLongitude = (int) (resultAddress.getLongitude() * 1E6);
	            userLocation = new GeoPoint(userLatitude, userLongitude);	    	    
	        }
	    } catch (IOException e) { 
	    	Log.e("IO Error", e.toString());
	    }
    }
    
    /*
     * Update screen map including person and pub overlays.
     */
    private void updateScreenMap() {    	
    	
    	updatePersonOverlay();  // update person marker and radius circle     	 
    	updatePubOverlay();   // update pub marker
    	updateInfoOverlay(); // update info status
    	
		// Set ZoomLevel according to the Radius
		setZoomLevelOnRadius(radius);
    }
    
    /* 
     *  Method for creating info status, how many pubs are displayed.
     */
	private void updateInfoOverlay() {
		String infoStatus;
		// Count displayed pubs
		pubList = SingleCache.getInstance().getCacheContent(); 
		int count = pubList.size();
		
		if(count == 0){
			infoStatus = "no pubs";
		} else if (count == 1) {
			infoStatus = "1 pub";
		} else
			infoStatus = count + " pubs"; 
			
		// Set new info status about the pubs
		mapViewer.setInfoStatus(infoStatus);
		
	}

	/*
	 * Update person location and radius on map.
	 */
    private void updatePersonOverlay() {
    	
    	// Set new userLocation and radius
		mapViewer.setPersonLocation(userLocation, radius);
		
		// Animate to the person marker
		mapViewer.getMapView().getController().animateTo(userLocation);
    }
    
    /*
     * Update the pubs on map.
     */
    private void updatePubOverlay() {
		
		List<Map<String, String>> pubList = SingleCache.getInstance().getCacheContent();
		
		// access the cache
		Iterator<Map<String, String>> iter = pubList.iterator();
		Map<String, String> pub = null;
		List<OverlayItem> pubItems = new ArrayList<OverlayItem>();
		while (iter.hasNext()) {
			pub = iter.next();
			int pubLatitude = Integer.parseInt(pub.get("latitude"));
			int pubLongitude = Integer.parseInt(pub.get("longitude"));
			GeoPoint pubPoint = new GeoPoint(pubLatitude, pubLongitude);

			// set a marker for each pub
			String pubId = pub.get("id");
			String pubName = pub.get("name");
			pubItems.add(new OverlayItem(pubPoint, pubName, pubId));
		}
		
		mapViewer.setPubLocations(pubItems);		
    }
    
    /*
     * Method calls service with specific parameters
     * and then extracts retrieved data.
     */
    private void callService() {
    	
    	if (userLocation != null) {
    		
	    	// Prepare the service call
			String endpoint = getString(R.string.service_endpoint);
			
			// Set up the parameters
			Map<String, String> params = new HashMap<String, String>();
			String latitude = String.valueOf(userLocation.getLatitudeE6());
			String longitude = String.valueOf(userLocation.getLongitudeE6());
			params.put("x", latitude);
			params.put("y", longitude);
			params.put("r", new Double((double)radius/1000).toString());  // convert radius into kilometers
			
			// Call the service and receive the raw data (JSON string)
			String jsonString = AndroidIOUtil.getHttpGetRequestResponse(endpoint, params);
			
			// Store the raw data to cache (i.e. write to a file)
			AndroidIOUtil.writeDataToFile("pub.txt", jsonString, this);
			
			// Construct the raw data into a data structure (i.e. an array of map)
			pubList = JSONUtil.getListFromJsonString(jsonString);
			
			// Save into single cache (temporary cache for applying preference)
			SingleCache.getInstance().setCacheContent(pubList);
					
			// Finally update the screen
			updateScreenMap();
    	}
    }
}